﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace NGE
{
    public class Net2
    {
        internal class DatagramAnalyzer
        {
            public DatagramAnalyzer(byte[] endtag)
            {
                endTag = endtag;
            }
            public DatagramAnalyzer(string endtag)
            {
                endTag = Encoding.Default.GetBytes(endtag);
            }
            public DatagramAnalyzer()
            {
                endTag = new byte[] { 0xff, 0xaa, 0x55, 0x00, 0x55, 0xaa, 0xff };
            }

            byte[] endTag;
            public byte[] EndTag
            {
                get
                {
                    return endTag;
                }
            }

            public List<byte[]> Analyze(ref byte[] datagram)
            {
                List<byte[]> ds = new List<byte[]>();
                while (true)
                {
                    int i = Util.ByteArrayContainsByteArray(datagram, endTag);

                    if (i < 0)
                    {
                        break;
                    }
                    else
                    {
                        byte[] temp = Util.ByteArrayCut(datagram, 0, i);
                        ds.Add(temp);
                        datagram = Util.ByteArrayCut(datagram, i + endTag.Length, datagram.Length - (i + endTag.Length));
                    }
                }
                return ds;
            }
        }
        public class ServerEventArgs : EventArgs
        {
            public int SessionIndex;
            public Session Session;
            public ServerEventArgs(int index, Session s)
            {
                Session = s;
                SessionIndex = index;
            }
        }
        public class ClientEventArgs : EventArgs
        {
            public Session Session;
            public ClientEventArgs(Session s)
            {
                Session = s;
            }
        }
        public enum SessionTerminateType
        {
            Normal,
            Force,
        }
        public delegate void ServerEvent(object sender, ServerEventArgs e);
        public delegate void ClientEvent(object sender, ClientEventArgs e);
        public class Session : IDisposable
        {
            public Session(Socket s)
            {
                Socket = s;
                ripaddr = Socket.RemoteEndPoint.ToString();
                lipaddr = Socket.LocalEndPoint.ToString();
                handle = s.Handle;
            }

            string ripaddr;
            string lipaddr;
            IntPtr handle;

            public byte[] Data;

            public string CPUID;
            public string HardDiskID;
            public string MainBoardID;
            public string OS;
            public string OSID;
            public string MACAddress;
            public string PCName;
            public string UserName;
            public SessionTerminateType TerminateType;
            public Socket Socket;
            public IntPtr Handle
            {
                get
                {
                    return handle;
                }
            }
            public string RemoteIPAddress
            {
                get
                {
                    return ripaddr;
                }
            }
            public string LocalIPAddress
            {
                get
                {
                    return lipaddr;
                }
            }

            public void Terminate(SessionTerminateType type)
            {
                TerminateType = type;
                if (Socket.Connected)
                {
                    Socket.Disconnect(true);
                }
                Socket.Close();
            }

            public void Terminate()
            {
                Terminate(SessionTerminateType.Normal);
            }

            public void Dispose()
            {
                Terminate();
                Data = null;
            }
        }
        public class Sessions : IDisposable
        {
            List<Session> sessions = new List<Session>();
            public int Count
            {
                get
                {
                    return sessions.Count;
                }
            }
            public int GetIndex(IntPtr handle)
            {
                for (int i = 0; i < sessions.Count; i++)
                {
                    if (sessions[i].Handle == handle)
                    {
                        return i;
                    }
                }
                return -1;
            }
            public int GetIndex(Session s)
            {
                return GetIndex(s.Handle);
            }
            public void Add(Session s)
            {
                if (GetIndex(s) == -1)
                {
                    sessions.Add(s);
                }
            }
            public void Del(int i)
            {
                sessions[i].Dispose();
                sessions.RemoveAt(i);
            }

            public void Del(Session s)
            {
                Del(GetIndex(s));
            }
            public Session this[int i]
            {
                get
                {
                    return sessions[i];
                }
                set
                {
                    sessions[i] = value;
                }
            }
            public Session this[IntPtr handle]
            {
                get
                {
                    return this[GetIndex(handle)];
                }
                set
                {
                    this[GetIndex(handle)] = value;
                }
            }
            public void Clear()
            {
                sessions.Clear();
            }
            public void Dispose()
            {
                foreach (Session s in sessions)
                {
                    s.Dispose();
                }
                Clear();
            }
        }

        public class Server
        {
            public Server(int port)
            {
                datagramAnalyzer = new DatagramAnalyzer();
                sessions = new Sessions();

                rbuffer = new byte[1024 * 1024];
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);

                OnClientConnected = new ServerEvent(Server_OnClientConnected);
                OnClientDisconnected = new ServerEvent(Server_OnClientDisconnected);

                serverSocket.Bind(ipEndPoint);
            }

            void Server_OnClientConnected(object sender, ServerEventArgs e)
            {
                sessions.Add(e.Session);
            }

            void Server_OnClientDisconnected(object sender, ServerEventArgs e)
            {
                sessions.Del(e.Session);
            }

            DatagramAnalyzer datagramAnalyzer;
            byte[] rbuffer;
            Sessions sessions;
            Socket serverSocket;
            bool isRunning;

            public int Capacity = 100;
            public event ServerEvent OnDataReceived;
            public event ServerEvent OnDataSent;
            public event ServerEvent OnClientConnected;
            public event ServerEvent OnClientDisconnected;
            public event ServerEvent OnServerFull;

            public bool IsRunning
            {
                get
                {
                    return isRunning;
                }
            }
            public int ClientCount
            {
                get
                {
                    return sessions.Count;
                }
            }

            public Session GetSession(int index)
            {
                return sessions[index];
            }
            public Session GetSession(IntPtr handle)
            {
                return sessions[handle];
            }
            public void Start()
            {
                isRunning = true;
                serverSocket.Listen(5);
                serverSocket.BeginAccept(new AsyncCallback(listenFunc), serverSocket);
            }
            void listenFunc(IAsyncResult iar)
            {
                if (isRunning == false)
                {
                    return;
                }

                Socket socket = (Socket)iar.AsyncState;
                Socket clientSocket = serverSocket.EndAccept(iar);
                Session s = new Session(clientSocket);

                if (ClientCount < Capacity)
                {
                    //sessions.Add(s);
                    clientSocket.BeginReceive(rbuffer, 0, rbuffer.Length, SocketFlags.None, new AsyncCallback(receiveFunc), clientSocket);
                    if (OnClientConnected != null)
                    {
                        OnClientConnected(this, new ServerEventArgs( sessions.Count,s));
                    }
                }
                else
                {
                    if (OnServerFull != null)
                    {
                        OnServerFull(this, new ServerEventArgs(-1, s));
                    }
                }

                socket.BeginAccept(new AsyncCallback(listenFunc), socket);
            }

            public void Stop()
            {
                isRunning = false;
                if (serverSocket.Connected)
                {
                    //socket.Shutdown(SocketShutdown.Both);
                    serverSocket.Disconnect(true);
                }
                serverSocket.Close();
                DisconnectAll();
            }
            public void DisconnectAll()
            {
                sessions.Dispose();
            }
            public void Send(Session session, object data)
            {
                Send(session, Util.Compress(Util.Serialize(data)));
            }
            public void Send(Session session, byte[] data)
            {
                if (session.Socket == null)
                {
                    return;
                }

                data = Util.ByteArrayCombine(data, datagramAnalyzer.EndTag);

                session.Socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(sendFunc), session.Socket);
            }
            void sendFunc(IAsyncResult iar)
            {
                Socket socket = ((Socket)iar.AsyncState);
                int i = sessions.GetIndex(socket.Handle);
                Session session = sessions[i];
                int count = socket.EndSend(iar);
                if (OnDataSent != null)
                {
                    OnDataSent(this, new ServerEventArgs(i, session));
                }
            }
            void receiveFunc(IAsyncResult iar)
            {
                Socket socket = ((Socket)iar.AsyncState);
                int i = sessions.GetIndex(socket.Handle);
                Session session = sessions[i];
                try
                {
                    int count = socket.EndReceive(iar);
                    if (count > 0)
                    {
                        //sessions[i].Data = new byte[count];
                        //Array.Copy(rbuffer, 0, sessions[i].Data, 0, count);
                        //if (OnDataReceived != null)
                        //{
                        //    OnDataReceived(this, new NetEventArgs(sessions[i]));
                        //}
                        byte[] rbuff = new byte[count];
                        Array.Copy(rbuffer, 0, rbuff, 0, count);
                        rbuff = Util.ByteArrayCombine(session.Data, rbuff);

                        List<byte[]> tmp = datagramAnalyzer.Analyze(ref rbuff);
                        foreach (byte[] bytes in tmp)
                        {
                            session.Data = Util.Decompress(bytes);
                            if (OnDataReceived != null)
                            {
                                OnDataReceived(this, new ServerEventArgs(i,session));
                            }
                        }
                        session.Data = rbuff;
                    }
                    else
                    {
                        session.Terminate();
                        if (OnClientDisconnected != null)
                        {
                            OnClientDisconnected(this, new ServerEventArgs(i,session));
                        }
                    }
                    socket.BeginReceive(rbuffer, 0, rbuffer.Length, SocketFlags.None, new AsyncCallback(receiveFunc), socket);
                }
                catch (SocketException ex)
                {
                    if (ex.ErrorCode == 10054)
                    {
                        //客户端强制关闭
                        session.Terminate(SessionTerminateType.Force);
                        if (OnClientDisconnected != null)
                        {
                            OnClientDisconnected(this, new ServerEventArgs(i, session));
                        }
                    }
                }
                catch (ObjectDisposedException ex)
                {
                    //这里的实现不够优雅
                    //当调用CloseSession()时,会结束数据接收,但是数据接收
                    //处理中会调用int recv = client.EndReceive(iar);
                    //就访问了CloseSession()已经处置的对象
                    //我想这样的实现方法也是无伤大雅的.
                    if (ex != null)
                    {
                        ex = null;
                    }
                    session.Terminate(SessionTerminateType.Force);
                    if (OnClientDisconnected != null)
                    {
                        OnClientDisconnected(this, new ServerEventArgs(i, session));
                    }
                }

            }
        }

        public class Client
        {
            public Client()
            {
                IsConnected = false;
                datagramAnalyzer = new DatagramAnalyzer();
                //session = new Session();
                rbuffer = new byte[1024 * 1024];
            }
            //List<ArraySegment<byte>> buffer = new List<ArraySegment<byte>>();

            byte[] rbuffer;
            Session session;
            DatagramAnalyzer datagramAnalyzer;

            public event ClientEvent OnConnected;
            public event ClientEvent OnDisconnected;

            public event ClientEvent OnDataReceived;
            public event ClientEvent OnDataSent;
            public bool IsConnected;

            public void Connect(string ip, int port)
            {
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPAddress[] ipa = Dns.GetHostAddresses(ip);
                IPEndPoint iep;
                if (ipa.Length > 0)
                {
                    iep = new IPEndPoint(IPAddress.Parse(ipa[ipa.Length - 1].ToString()), port);
                }
                else
                {
                    iep = new IPEndPoint(IPAddress.Parse(ip), port);
                }
                socket.BeginConnect(iep, new AsyncCallback(connectFunc), socket);
            }
            public void ConnectSync(string ip, int port)
            {
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(ip, port);
                IsConnected = true;
            }
            void connectFunc(IAsyncResult iar)
            {
                Socket socket = ((Socket)iar.AsyncState);
                //try
                {
                    socket.EndConnect(iar);

                    session = new Session(socket);
                    IsConnected = true;
                    if (OnConnected != null)
                    {

                        OnConnected(this, new ClientEventArgs(session));
                    }
                    socket.BeginReceive(rbuffer, 0, rbuffer.Length, SocketFlags.None, new AsyncCallback(receiveFunc), socket);
                }
                //catch (SocketException ex)
                //{
                //    if (ex.ErrorCode == 10061 || ex.ErrorCode == 10057)
                //    {
                //        //throw new NotImplementedException("服务器未开启");
                //        System.Windows.Forms.MessageBox.Show("请确认可连入Internet或服务器已开启");
                //        return;
                //    }
                //}
            }
            void receiveFunc(IAsyncResult iar)
            {
                Socket socket = ((Socket)iar.AsyncState);
                try
                {
                    int count = socket.EndReceive(iar);
                    if (count > 0)
                    {
                        //session.Data = new byte[count];
                        //Array.Copy(rbuffer, 0, session.Data, 0, count);
                        //if (OnDataReceived != null)
                        //{
                        //    OnDataReceived(this, new NetEventArgs(session));
                        //}                 

                        //sessions[i].Data = new byte[count];
                        //Array.Copy(rbuffer, 0, sessions[i].Data, 0, count);
                        //if (OnDataReceived != null)
                        //{
                        //    OnDataReceived(this, new NetEventArgs(sessions[i]));
                        //}

                        byte[] rbuff = new byte[count];
                        Array.Copy(rbuffer, 0, rbuff, 0, count);
                        rbuff = Util.ByteArrayCombine(session.Data, rbuff);

                        List<byte[]> tmp = datagramAnalyzer.Analyze(ref rbuff);
                        foreach (byte[] bytes in tmp)
                        {
                            session.Data = Util.Decompress(bytes);
                            if (OnDataReceived != null)
                            {
                                OnDataReceived(this, new ClientEventArgs(session));
                            }
                        }
                        session.Data = rbuff;
                    }
                    else
                    {
                        Disconnect();
                    }
                    socket.BeginReceive(rbuffer, 0, rbuffer.Length, SocketFlags.None, new AsyncCallback(receiveFunc), socket);
                }
                catch (SocketException ex)
                {
                    if (ex.ErrorCode == 10054)
                    {
                        //客户端强制关闭
                        Disconnect();
                    }
                }
                catch (ObjectDisposedException ex)
                {
                    //这里的实现不够优雅
                    //当调用CloseSession()时,会结束数据接收,但是数据接收
                    //处理中会调用int recv = client.EndReceive(iar);
                    //就访问了CloseSession()已经处置的对象
                    //我想这样的实现方法也是无伤大雅的.
                    if (ex != null)
                    {
                        ex = null;
                    }
                    Disconnect();
                }
            }
            public void Send(object data)
            {
                Send(Util.Compress(Util.Serialize(data)));
            }
            public void Send(byte[] data)
            {
                if (session == null)
                {
                    return;
                }
                if (session.Socket == null)
                {
                    return;
                }
                if (session.Socket.Connected == false)
                {
                    return;
                }

                data = Util.ByteArrayCombine(data, datagramAnalyzer.EndTag);

                session.Socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(sendFunc), session.Socket);
            }
            void sendFunc(IAsyncResult iar)
            {
                Socket socket = ((Socket)iar.AsyncState);
                int count = socket.EndSend(iar);
                if (OnDataSent != null)
                {
                    OnDataSent(this, new ClientEventArgs(session));
                }
            }

            public void Disconnect()
            {
                session.Terminate(SessionTerminateType.Force);
                if (OnDisconnected != null)
                {
                    OnDisconnected(this, new ClientEventArgs(session));
                }
                IsConnected = false;
            }
        }
    }
}
